﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Automation;

namespace Codeplex.UiAutomationExtension
{
    #region UIAutomationExtension
    /// <summary>
    /// UI オートメーション用の拡張メソッド群。
    /// </summary>
    public static class UIAutomationExtension
    {
        #region Root
        /// <summary>
        /// <see cref="System.Windows.Automation.AutomationElement"/> のルート要素
        /// （デスクトップ）を取得する。
        /// </summary>
        public static AutomationElement Root
        {
            get { return AutomationElement.RootElement; }
        }
        #endregion

        #region FromName

        /// <summary>
        /// 指定した名前を持つ UI オートメーション要素を取得する。
        /// </summary>
        /// <param name="target">検索対象の要素の親要素。</param>
        /// <param name="name">検索する要素の名前。</param>
        /// <param name="retryCount">要素が見つからなかった場合に検索を試行する回数。デフォルトは 50 回。</param>
        /// <returns>指定した名前を持つ UI オートメーション要素</returns>
        public static AutomationElement FromName(this AutomationElement target, string name, int retryCount = 50)
        {
            var count = 0;
            AutomationElement child = null;
            var condition = new PropertyCondition(AutomationElement.NameProperty, name);
            while ((child = target.FindFirst(TreeScope.Children, condition)) == null && count++ < retryCount)
                Thread.Sleep(100);

            return child;
        }

        /// <summary>
        /// 指定した名前とクラス名を持つ UI オートメーション要素を取得する。
        /// </summary>
        /// <param name="target">検索対象の要素の親要素。</param>
        /// <param name="name">検索する要素の名前。</param>
        /// <param name="className">検索する要素のクラス名。</param>
        /// <param name="retryCount">要素が見つからなかった場合に検索を試行する回数。デフォルトは 50 回。</param>
        /// <returns>指定した名前を持つ UI オートメーション要素</returns>
        public static AutomationElement FromName(this AutomationElement target, string name, string className, int retryCount = 50)
        {
            var count = 0;
            AutomationElement child = null;
            var c1 = new PropertyCondition(AutomationElement.NameProperty, name);
            var c2 = new PropertyCondition(AutomationElement.ClassNameProperty, className);
            var condition = new AndCondition(c1, c2);
            while ((child = target.FindFirst(TreeScope.Children, condition)) == null && count++ < retryCount)
                Thread.Sleep(100);

            return child;
        }

        #endregion

        #region FromId

        /// <summary>
        /// 指定した ID を持つ UI オートメーション要素を取得する。
        /// </summary>
        /// <param name="target">検索対象の要素の親要素。</param>
        /// <param name="id">検索する要素の ID。</param>
        /// <param name="retryCount">要素が見つからなかった場合に検索を試行する回数。デフォルトは 50 回。</param>
        /// <returns>指定した ID を持つ UI オートメーション要素</returns>
        public static AutomationElement FromId(this AutomationElement target, string id, int retryCount = 50)
        {
            var count = 0;
            AutomationElement child = null;
            var condition = new PropertyCondition(AutomationElement.AutomationIdProperty, id);
            while ((child = target.FindFirst(TreeScope.Children, condition)) == null && count++ < retryCount)
                Thread.Sleep(100);

            return child;
        }

        /// <summary>
        /// 指定した ID とクラス名を持つ UI オートメーション要素を取得する。
        /// </summary>
        /// <param name="target">検索対象の要素の親要素。</param>
        /// <param name="id">検索する要素の ID。</param>
        /// <param name="className">検索する要素のクラス名。</param>
        /// <param name="retryCount">要素が見つからなかった場合に検索を試行する回数。デフォルトは 50 回。</param>
        /// <returns>指定した ID を持つ UI オートメーション要素</returns>
        public static AutomationElement FromId(this AutomationElement target, string id, string className, int retryCount = 50)
        {
            var count = 0;
            AutomationElement child = null;
            var c1 = new PropertyCondition(AutomationElement.AutomationIdProperty, id);
            var c2 = new PropertyCondition(AutomationElement.ClassNameProperty, className);
            var condition = new AndCondition(c1, c2);
            while ((child = target.FindFirst(TreeScope.Children, condition)) == null && count++ < retryCount)
                Thread.Sleep(100);

            return child;
        }

        #endregion

        #region ElementAt
        /// <summary>
        /// 指定した要素の子のうち、index 番目に出現する UI オートメーション要素を取得する。
        /// </summary>
        /// <param name="target">検索対象の要素の親要素。</param>
        /// <param name="index">検索対象の要素の出現順。</param>
        /// <param name="retryCount">要素が見つからなかった場合に検索を試行する回数。デフォルトは 50 回。</param>
        /// <returns>検索対象の UI オートメーション要素。</returns>
        public static AutomationElement ElementAt(this AutomationElement target, int index, int retryCount = 50)
        {
            var count = 0;
            AutomationElementCollection children = null;
            var condition = PropertyCondition.TrueCondition;
            while ((children = target.FindAll(TreeScope.Children, condition)) == null && count++ < retryCount)
                Thread.Sleep(100);

            return children[index];
        }
        #endregion

        #region Elements
        /// <summary>
        /// 指定した要素のすべての子要素を取得する。
        /// </summary>
        /// <param name="target">検索対象の要素の親要素。</param>
        /// <param name="retryCount">要素が見つからなかった場合に検索を試行する回数。デフォルトは 50 回。</param>
        /// <returns>指定した要素のすべての子要素。</returns>
        public static IEnumerable<AutomationElement> Elements(this AutomationElement target, int retryCount = 50)
        {
            var count = 0;
            AutomationElementCollection children = null;
            var condition = PropertyCondition.TrueCondition;
            while ((children = target.FindAll(TreeScope.Children, condition)) == null && count++ < retryCount)
                Thread.Sleep(100);

            foreach (AutomationElement child in children)
                yield return child;
        }
        #endregion

        #region AsValue
        /// <summary>
        /// UI オートメーション要素の <see cref="System.Windows.Automation.ValuePattern"/> を取得する。
        /// </summary>
        /// <param name="target">UI オートメーション要素。</param>
        /// <returns>UI オートメーション要素の <see cref="System.Windows.Automation.ValuePattern"/>。</returns>
        public static ValuePattern AsValue(this AutomationElement target)
        {
            return target.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
        }
        #endregion

        #region AsInvoke
        /// <summary>
        /// UI オートメーション要素の <see cref="System.Windows.Automation.InvokePattern"/> を取得する。
        /// </summary>
        /// <param name="target">UI オートメーション要素。</param>
        /// <returns>UI オートメーション要素の <see cref="System.Windows.Automation.InvokePattern"/>。</returns>
        public static InvokePattern AsInvoke(this AutomationElement target)
        {
            return target.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
        }
        #endregion

        #region AsListItemCollection
        /// <summary>
        /// UI オートメーション要素の <see cref="System.Windows.Automation.ControlType.ListItem"/> を取得する。
        /// </summary>
        /// <param name="target">UI オートメーション要素。</param>
        /// <returns>UI オートメーション要素の <see cref="System.Windows.Automation.ControlType.ListItem"/>。</returns>
        public static AutomationElementCollection AsListItemCollection(this AutomationElement target)
        {
            return target.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));
        }
        #endregion

        #region AsSelectionItem
        /// <summary>
        /// UI オートメーション要素の <see cref="System.Windows.Automation.SelectionItemPattern"/> を取得する。
        /// </summary>
        /// <param name="target">UI オートメーション要素。</param>
        /// <returns>UI オートメーション要素の <see cref="System.Windows.Automation.SelectionItemPattern"/>。</returns>
        public static SelectionItemPattern AsSelectionItem(this AutomationElement target)
        {
            return target.GetCurrentPattern(SelectionItemPattern.Pattern) as SelectionItemPattern;
        }
        #endregion

        #region AsSelectionItemCollection
        /// <summary>
        /// UI オートメーション要素の <see cref="System.Windows.Automation.SelectionItemPattern"/> を取得する。
        /// </summary>
        /// <param name="target">UI オートメーション要素。</param>
        /// <returns>UI オートメーション要素の <see cref="System.Windows.Automation.SelectionItemPattern"/>。</returns>
        public static IEnumerable<SelectionItemPattern> AsSelectionItemCollection(this AutomationElement target)
        {
            return target.AsListItemCollection()
                .OfType<AutomationElement>()
                .Select(l => l.AsSelectionItem());
        }
        #endregion

        #region AsExpandCollapse
        /// <summary>
        /// UI オートメーション要素の <see cref="System.Windows.Automation.ExpandCollapsePattern"/> を取得する。
        /// </summary>
        /// <param name="target">UI オートメーション要素。</param>
        /// <returns>UI オートメーション要素の <see cref="System.Windows.Automation.ExpandCollapsePattern"/>。</returns>
        public static ExpandCollapsePattern AsExpandCollapse(this AutomationElement target)
        {
            return target.GetCurrentPattern(ExpandCollapsePattern.Pattern) as ExpandCollapsePattern;
        }
        #endregion
    }
    #endregion
}
